﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Threading;
using System.Windows.Forms;
using NExplorer.Commands;
using NExplorer.Controls;
using NExplorer.Properties;

namespace NExplorer
{
	sealed internal partial class MainForm : Form
	{
		#region P/Invoke stuff

        private const int EM_SETCUEBANNER = 0x1501;

		[DllImport("dwmapi.dll", PreserveSig = false)]
		public static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);

        [DllImport("user32", CharSet = CharSet.Unicode)]
        internal static extern IntPtr SendMessage(IntPtr hWnd, int message, IntPtr wParam, string lParam);

		[StructLayout(LayoutKind.Sequential)]
		public struct MARGINS
		{
			public int cxLeftWidth;
			public int cxRightWidth;
			public int cyTopHeight;
			public int cyBottomHeight;
		}

		#endregion

		private readonly ExplorerService				_explorerService;
		private readonly Dictionary<string,string>		_dirShortcuts = new Dictionary<string,string>();
		private readonly bool							_isAdministrator;

		public MainForm()
		{
			InitializeComponent();

			_dirShortcuts.Add("computer", null);
			_dirShortcuts.Add("documents", Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
			_dirShortcuts.Add("my documents", Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
			_dirShortcuts.Add("music", Environment.GetFolderPath(Environment.SpecialFolder.MyMusic));
			_dirShortcuts.Add("my music", Environment.GetFolderPath(Environment.SpecialFolder.MyMusic));
			_dirShortcuts.Add("desktop", Environment.GetFolderPath(Environment.SpecialFolder.Desktop));

			showFullPathMenuItem.Checked = Settings.Default.FullPathInWindowTitle;
			showFullPathMenuItem.CheckedChanged += delegate { Settings.Default.FullPathInWindowTitle = showFullPathMenuItem.Checked; };
			loadedAddInsMenuItem.Click += delegate { ShowLoadedAddIns(); };

			if (Environment.OSVersion.Version.Major >= 6)
			{
				Font = new Font("Segoe UI", 9);
			}
			else
			{
				Font = SystemFonts.DialogFont;
			}

		    mainToolStrip.Font = SystemFonts.MenuFont;
		    menuStrip.Font = SystemFonts.MenuFont;

			Icon = Resources.Computer;

			toolStrip.ButtonWithShortcutKeysAdded += AddButtonWithShortcutKeysToHiddenMenu;
			toolStrip.ButtonWithShortcutKeysRemoved += RemoveButtonWithShortcutKeysFromHiddenMenu;

			_explorerService = (ExplorerService)ServiceManager.Explorer;
			_explorerService.CurrentDirectoryChanged += WhenCurrentDirectoryChanged;

			goToMenuItem.DropDownItems.Add(new ExplorerMenuItem(new NavigateBackCommand(), Keys.Left | Keys.Alt));
			goToMenuItem.DropDownItems.Add(new ExplorerMenuItem(new NavigateForwardCommand(), Keys.Right| Keys.Alt));
			goToMenuItem.DropDownItems.Add(new ExplorerMenuItem(new UpOneFolderCommand(), Keys.Up | Keys.Alt));
			
			listView.ItemCountChanged += delegate { UpdateItemCount(); };

            SendMessage(searchTextBox.TextBox.Handle, EM_SETCUEBANNER, IntPtr.Zero, "Search");
            searchTextBox.Font = new Font(searchTextBox.Font, FontStyle.Italic);

			MainMenuStrip.Renderer = Utilities.GetMenuStripRenderer();

			AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
			_isAdministrator = ((WindowsPrincipal)Thread.CurrentPrincipal).IsInRole(WindowsBuiltInRole.Administrator);
		}

		private void ShowLoadedAddIns()
		{
			List<string> addInNames = new List<string>();
			AddInManager.LoadedAddIns.ForEach(addIn => addInNames.Add(addIn.Name + ": " + addIn.Description));

			MessageBox.Show("Loaded AddIns:\n\n" + string.Join("\n", addInNames.ToArray()), "Loaded AddIns", MessageBoxButtons.OK, MessageBoxIcon.Information);
		}

		private void AddButtonWithShortcutKeysToHiddenMenu(object sender, EventArgs<ExplorerToolBarButton> e)
		{
			ExplorerMenuItem menuItem = new ExplorerMenuItem(e.Item.Command);
			menuItem.ShortcutKeys = e.Item.Keys;

			shortcutKeysMenuItem.DropDownItems.Add(menuItem);
		}

		private void RemoveButtonWithShortcutKeysFromHiddenMenu(object sender, EventArgs<ExplorerToolBarButton> e)
		{
			ToolStripMenuItem[] items = hiddenFoldersAndFilesToolStripMenuItem.DropDownItems.ToArray<ToolStripMenuItem>();

			foreach (ExplorerMenuItem menuItem in items)
			{
				if (menuItem.Command == e.Item.Command)
				{
					((ToolStripDropDownItem)menuItem.OwnerItem).DropDownItems.Remove(menuItem);
				}
			}
		}

		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);

			toolStrip.Renderer = Utilities.GetToolBarRenderer();

			SetVistaMode(false);

			_explorerService.NavigateTo(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Desktop)));
		}

		public IMainToolBar ToolBar
		{
			get { return toolStrip;}
		}

		private void WhenCurrentDirectoryChanged(object sender, EventArgs e)
		{
			DirectoryInfo dirInfo = _explorerService.CurrentDirectory;

		    // Back and Forward buttons
			HistoryList historyList = _explorerService.HistoryList;
			backButton.Enabled = historyList.CanMoveBack;
		    backButton.ToolTipText = historyList.GetPreviousText();
			forwardButton.Enabled = historyList.CanMoveForward;
		    forwardButton.ToolTipText = historyList.GetNextText();

			// Window title
			if (Settings.Default.FullPathInWindowTitle)
			{
				Text = dirInfo.FullName;
			}
			else
			{
				Text = dirInfo.Name;
			}

			Text += " - " + Application.ProductName + (_isAdministrator ? " (Administrator)" : "");

			// Icon
			try
			{
				Bitmap bitmap = (Bitmap)treeView.ImageList.Images[treeView.SelectedNode.ImageIndex];
				Icon = Icon.FromHandle(bitmap.GetHicon());
			}
			catch
			{
				// Can occastionally run into problems in the above code, so just leave the icon
				// as it is. 
			}

			urlTextBox.Text = dirInfo.FullName;
			listView.Focus();
		}

		private void UpdateItemCount()
		{
			int count = listView.Items.Count;
			statusStrip.Items[0].Text = string.Format("{0:n0} item{1}", count, (count != 1 ? "s" : ""));
		}

		private void SetVistaMode(bool enabled)
		{
			if (!DesignMode && Utilities.IsCompositionEnabled)
			{
				MARGINS m = new MARGINS();
				m.cyTopHeight = enabled ? mainToolStrip.Height : 0;

				DwmExtendFrameIntoClientArea(Handle, ref m);
			}
		}

		private void toolStripButton1_Click(object sender, EventArgs e)
		{
			toolStripButton1.Checked = menuStrip.Visible = !menuStrip.Visible;
		}

		private void goButton_Click(object sender, EventArgs e)
		{
			string directory = urlTextBox.Text;
			
			// Remove any spaces and/or quotes around the name
			directory = directory.Trim().Trim('"');

			// Check for environment variable
			directory = Environment.ExpandEnvironmentVariables(directory);

			DirectoryInfo dirInfo;

			if (_dirShortcuts.ContainsKey(directory.ToLower()))
			{
				string path = _dirShortcuts[directory.ToLower()];

				if (path == null)
				{
					dirInfo = null;
				}
				else
				{
					dirInfo = new DirectoryInfo(path);
				}
			}
			else
			{
				dirInfo = new DirectoryInfo(directory);
			}

			_explorerService.NavigateTo(dirInfo);
		}

		protected override void OnKeyDown(KeyEventArgs e)
		{
			if (e.Alt && e.KeyCode == Keys.D)
			{
				urlTextBox.Focus();
			    e.SuppressKeyPress = true;
			}
            else if (e.KeyCode == Keys.Delete)
            {
                ServiceManager.FileOperations.SendItemsToRecycleBin(ServiceManager.Explorer.SelectedItems);
            }
            else if (e.KeyCode == Keys.Back)
            {
                if (!urlTextBox.Focused && !searchTextBox.Focused)
                {
                    if (e.Shift)
                    {
                        if (_explorerService.CanNavigateForward)
                        {
                            _explorerService.NavigateForward();
                        }
                    }
                    else
                    {
                        if (_explorerService.CanNavigateBack)
                        {
                            _explorerService.NavigateBack();
                        }
                    }
                }
            }
            else
            {
                base.OnKeyDown(e);
            }
		}

		private void urlTextBox_KeyUp(object sender, KeyEventArgs e)
		{
			if (e.KeyCode == Keys.Enter)
			{
				goButton.PerformClick();
				e.Handled = true;
			}
		}

		private void hiddenFoldersAndFilesToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
		{
			string text = Application.ProductName + " must be restarted for this change. Continue?";
			
			if (MessageBox.Show(text, "Restart?", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
			{
				Settings.Default.ShowHiddenFiles = hiddenFoldersAndFilesToolStripMenuItem.Checked;
				Settings.Default.Save();

				_explorerService.NavigateTo(new FileInfo(Application.ExecutablePath));
				
				Close();
			}
		}

		private void vistaHeaderToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
		{
			mainToolStrip.BackColor = vistaHeaderToolStripMenuItem.Checked ? Color.Black : SystemColors.Control;
			SetVistaMode(vistaHeaderToolStripMenuItem.Checked);
		}

		private void selectAllMenuItem_Click(object sender, EventArgs e)
		{
			ServiceManager.Explorer.SelectedItems = ServiceManager.Explorer.CurrentDirectory.GetFileSystemInfos();
		}

		private void refreshMenuItem_Click(object sender, EventArgs e)
		{
			ServiceManager.Explorer.Refresh();
		}

		private void aboutMenuItem_Click(object sender, EventArgs e)
		{
			new AboutForm().ShowDialog();
		}

		private void backButton_Click(object sender, EventArgs e)
		{
			_explorerService.NavigateBack();
		}

		private void forwardButton_Click(object sender, EventArgs e)
		{
			_explorerService.NavigateForward();
		}

        private void searchTextBox_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                try
                {
                    string path = null;

                    if (ServiceManager.Explorer.CurrentDirectory != null)
                    {
                        path = ServiceManager.Explorer.CurrentDirectory.FullName;
                        path = path.Replace(":", "%3a").Replace("\\", "%5c");
                        path = "&crumb=location:" + path;
                    }

                    string query = "search-ms:query=" + searchTextBox.Text + path;
                    Process.Start(query);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error while attempting to search: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private void searchTextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            if(e.KeyChar == (char) 13)
            {
                e.Handled = true;
            }
        }

        private void toolStrip1_Layout(object sender, LayoutEventArgs e)
        {
            int width = mainToolStrip.DisplayRectangle.Width;

            foreach (ToolStripItem tsi in mainToolStrip.Items)
            {
                if (tsi != urlTextBox)
                {
                    width -= tsi.Width;
                }
            }

            urlTextBox.Width = width - 5;
        }

        private void searchTextBox_TextChanged(object sender, EventArgs e)
        {
            string text = searchTextBox.Text.Trim();

            if (text.Length > 0 && searchTextBox.Font.Italic)
            {
                searchTextBox.Font = new Font(searchTextBox.Font, FontStyle.Regular);
            }
            else if (text.Length == 0 && !searchTextBox.Font.Italic)
            {
                searchTextBox.Font = new Font(searchTextBox.Font, FontStyle.Italic);
            }
        }
	}
}